home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / g_target.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-08-13  |  20.8 KB  |  810 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. #include "g_local.h"
  21.  
  22. /*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8)
  23. Fire an origin based temp entity event to the clients.
  24. "style"        type byte
  25. */
  26. void Use_Target_Tent (edict_t *ent, edict_t *other, edict_t *activator)
  27. {
  28.     gi.WriteByte (svc_temp_entity);
  29.     gi.WriteByte (ent->style);
  30.     gi.WritePosition (ent->s.origin);
  31.     gi.multicast (ent->s.origin, MULTICAST_PVS);
  32. }
  33.  
  34. void SP_target_temp_entity (edict_t *ent)
  35. {
  36.     ent->use = Use_Target_Tent;
  37. }
  38.  
  39.  
  40. //==========================================================
  41.  
  42. //==========================================================
  43.  
  44. /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable
  45. "noise"        wav file to play
  46. "attenuation"
  47. -1 = none, send to whole level
  48. 1 = normal fighting sounds
  49. 2 = idle sound level
  50. 3 = ambient sound level
  51. "volume"    0.0 to 1.0
  52.  
  53. Normal sounds play each time the target is used.  The reliable flag can be set for crucial voiceovers.
  54.  
  55. Looped sounds are allways atten 3 / vol 1, and the use function toggles it on/off.
  56. Multiple identical looping sounds will just increase volume without any speed cost.
  57. */
  58. void Use_Target_Speaker (edict_t *ent, edict_t *other, edict_t *activator)
  59. {
  60.     int        chan;
  61.  
  62.     if (ent->spawnflags & 3)
  63.     {    // looping sound toggles
  64.         if (ent->s.sound)
  65.             ent->s.sound = 0;    // turn it off
  66.         else
  67.             ent->s.sound = ent->noise_index;    // start it
  68.     }
  69.     else
  70.     {    // normal sound
  71.         if (ent->spawnflags & 4)
  72.             chan = CHAN_VOICE|CHAN_RELIABLE;
  73.         else
  74.             chan = CHAN_VOICE;
  75.         // use a positioned_sound, because this entity won't normally be
  76.         // sent to any clients because it is invisible
  77.         gi.positioned_sound (ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
  78.     }
  79. }
  80.  
  81. void SP_target_speaker (edict_t *ent)
  82. {
  83.     char    buffer[MAX_QPATH];
  84.  
  85.     if(!st.noise)
  86.     {
  87.         gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
  88.         return;
  89.     }
  90.     if (!strstr (st.noise, ".wav"))
  91.         Com_sprintf (buffer, sizeof(buffer), "%s.wav", st.noise);
  92.     else
  93.         strncpy (buffer, st.noise, sizeof(buffer));
  94.     ent->noise_index = gi.soundindex (buffer);
  95.  
  96.     if (!ent->volume)
  97.         ent->volume = 1.0;
  98.  
  99.     if (!ent->attenuation)
  100.         ent->attenuation = 1.0;
  101.     else if (ent->attenuation == -1)    // use -1 so 0 defaults to 1
  102.         ent->attenuation = 0;
  103.  
  104.     // check for prestarted looping sound
  105.     if (ent->spawnflags & 1)
  106.         ent->s.sound = ent->noise_index;
  107.  
  108.     ent->use = Use_Target_Speaker;
  109.  
  110.     // must link the entity so we get areas and clusters so
  111.     // the server can determine who to send updates to
  112.     gi.linkentity (ent);
  113. }
  114.  
  115.  
  116. //==========================================================
  117.  
  118. void Use_Target_Help (edict_t *ent, edict_t *other, edict_t *activator)
  119. {
  120.     if (ent->spawnflags & 1)
  121.         strncpy (game.helpmessage1, ent->message, sizeof(game.helpmessage2)-1);
  122.     else
  123.         strncpy (game.helpmessage2, ent->message, sizeof(game.helpmessage1)-1);
  124.  
  125.     game.helpchanged++;
  126. }
  127.  
  128. /*QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1
  129. When fired, the "message" key becomes the current personal computer string, and the message light will be set on all clients status bars.
  130. */
  131. void SP_target_help(edict_t *ent)
  132. {
  133.     if (deathmatch->value)
  134.     {    // auto-remove for deathmatch
  135.         G_FreeEdict (ent);
  136.         return;
  137.     }
  138.  
  139.     if (!ent->message)
  140.     {
  141.         gi.dprintf ("%s with no message at %s\n", ent->classname, vtos(ent->s.origin));
  142.         G_FreeEdict (ent);
  143.         return;
  144.     }
  145.     ent->use = Use_Target_Help;
  146. }
  147.  
  148. //==========================================================
  149.  
  150. /*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8)
  151. Counts a secret found.
  152. These are single use targets.
  153. */
  154. void use_target_secret (edict_t *ent, edict_t *other, edict_t *activator)
  155. {
  156.     gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
  157.  
  158.     level.found_secrets++;
  159.  
  160.     G_UseTargets (ent, activator);
  161.     G_FreeEdict (ent);
  162. }
  163.  
  164. void SP_target_secret (edict_t *ent)
  165. {
  166.     if (deathmatch->value)
  167.     {    // auto-remove for deathmatch
  168.         G_FreeEdict (ent);
  169.         return;
  170.     }
  171.  
  172.     ent->use = use_target_secret;
  173.     if (!st.noise)
  174.         st.noise = "misc/secret.wav";
  175.     ent->noise_index = gi.soundindex (st.noise);
  176.     ent->svflags = SVF_NOCLIENT;
  177.     level.total_secrets++;
  178.     // map bug hack
  179.     if (!stricmp(level.mapname, "mine3") && ent->s.origin[0] == 280 && ent->s.origin[1] == -2048 && ent->s.origin[2] == -624)
  180.         ent->message = "You have found a secret area.";
  181. }
  182.  
  183. //==========================================================
  184.  
  185. /*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8)
  186. Counts a goal completed.
  187. These are single use targets.
  188. */
  189. void use_target_goal (edict_t *ent, edict_t *other, edict_t *activator)
  190. {
  191.     gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
  192.  
  193.     level.found_goals++;
  194.  
  195.     if (level.found_goals == level.total_goals)
  196.         gi.configstring (CS_CDTRACK, "0");
  197.  
  198.     G_UseTargets (ent, activator);
  199.     G_FreeEdict (ent);
  200. }
  201.  
  202. void SP_target_goal (edict_t *ent)
  203. {
  204.     if (deathmatch->value)
  205.     {    // auto-remove for deathmatch
  206.         G_FreeEdict (ent);
  207.         return;
  208.     }
  209.  
  210.     ent->use = use_target_goal;
  211.     if (!st.noise)
  212.         st.noise = "misc/secret.wav";
  213.     ent->noise_index = gi.soundindex (st.noise);
  214.     ent->svflags = SVF_NOCLIENT;
  215.     level.total_goals++;
  216. }
  217.  
  218. //==========================================================
  219.  
  220.  
  221. /*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8)
  222. Spawns an explosion temporary entity when used.
  223.  
  224. "delay"        wait this long before going off
  225. "dmg"        how much radius damage should be done, defaults to 0
  226. */
  227. void target_explosion_explode (edict_t *self)
  228. {
  229.     float        save;
  230.  
  231.     gi.WriteByte (svc_temp_entity);
  232.     gi.WriteByte (TE_EXPLOSION1);
  233.     gi.WritePosition (self->s.origin);
  234.     gi.multicast (self->s.origin, MULTICAST_PHS);
  235.  
  236.     T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
  237.  
  238.     save = self->delay;
  239.     self->delay = 0;
  240.     G_UseTargets (self, self->activator);
  241.     self->delay = save;
  242. }
  243.  
  244. void use_target_explosion (edict_t *self, edict_t *other, edict_t *activator)
  245. {
  246.     self->activator = activator;
  247.  
  248.     if (!self->delay)
  249.     {
  250.         target_explosion_explode (self);
  251.         return;
  252.     }
  253.  
  254.     self->think = target_explosion_explode;
  255.     self->nextthink = level.time + self->delay;
  256. }
  257.  
  258. void SP_target_explosion (edict_t *ent)
  259. {
  260.     ent->use = use_target_explosion;
  261.     ent->svflags = SVF_NOCLIENT;
  262. }
  263.  
  264.  
  265. //==========================================================
  266.  
  267. /*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
  268. Changes level to "map" when fired
  269. */
  270. void use_target_changelevel (edict_t *self, edict_t *other, edict_t *activator)
  271. {
  272.     if (level.intermissiontime)
  273.         return;        // allready activated
  274.  
  275.     if (!deathmatch->value && !coop->value)
  276.     {
  277.         if (g_edicts[1].health <= 0)
  278.             return;
  279.     }
  280.  
  281.     // if noexit, do a ton of damage to other
  282.     if (deathmatch->value && !( (int)dmflags->value & DF_ALLOW_EXIT) && other != world)
  283.     {
  284.         T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
  285.         return;
  286.     }
  287.  
  288.     // if multiplayer, let everyone know who hit the exit
  289.     if (deathmatch->value)
  290.     {
  291.         if (activator && activator->client)
  292.             gi.bprintf (PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
  293.     }
  294.  
  295.     // if going to a new unit, clear cross triggers
  296.     if (strstr(self->map, "*"))    
  297.         game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
  298.  
  299.     BeginIntermission (self);
  300. }
  301.  
  302. void SP_target_changelevel (edict_t *ent)
  303. {
  304.     if (!ent->map)
  305.     {
  306.         gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
  307.         G_FreeEdict (ent);
  308.         return;
  309.     }
  310.  
  311.     // ugly hack because *SOMEBODY* screwed up their map
  312.    if((stricmp(level.mapname, "fact1") == 0) && (stricmp(ent->map, "fact3") == 0))
  313.        ent->map = "fact3$secret1";
  314.  
  315.     ent->use = use_target_changelevel;
  316.     ent->svflags = SVF_NOCLIENT;
  317. }
  318.  
  319.  
  320. //==========================================================
  321.  
  322. /*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8)
  323. Creates a particle splash effect when used.
  324.  
  325. Set "sounds" to one of the following:
  326.   1) sparks
  327.   2) blue water
  328.   3) brown water
  329.   4) slime
  330.   5) lava
  331.   6) blood
  332.  
  333. "count"    how many pixels in the splash
  334. "dmg"    if set, does a radius damage at this location when it splashes
  335.         useful for lava/sparks
  336. */
  337.  
  338. void use_target_splash (edict_t *self, edict_t *other, edict_t *activator)
  339. {
  340.     gi.WriteByte (svc_temp_entity);
  341.     gi.WriteByte (TE_SPLASH);
  342.     gi.WriteByte (self->count);
  343.     gi.WritePosition (self->s.origin);
  344.     gi.WriteDir (self->movedir);
  345.     gi.WriteByte (self->sounds);
  346.     gi.multicast (self->s.origin, MULTICAST_PVS);
  347.  
  348.     if (self->dmg)
  349.         T_RadiusDamage (self, activator, self->dmg, NULL, self->dmg+40, MOD_SPLASH);
  350. }
  351.  
  352. void SP_target_splash (edict_t *self)
  353. {
  354.     self->use = use_target_splash;
  355.     G_SetMovedir (self->s.angles, self->movedir);
  356.  
  357.     if (!self->count)
  358.         self->count = 32;
  359.  
  360.     self->svflags = SVF_NOCLIENT;
  361. }
  362.  
  363.  
  364. //==========================================================
  365.  
  366. /*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8)
  367. Set target to the type of entity you want spawned.
  368. Useful for spawning monsters and gibs in the factory levels.
  369.  
  370. For monsters:
  371.     Set direction to the facing you want it to have.
  372.  
  373. For gibs:
  374.     Set direction if you want it moving and
  375.     speed how fast it should be moving otherwise it
  376.     will just be dropped
  377. */
  378. void ED_CallSpawn (edict_t *ent);
  379.  
  380. void use_target_spawner (edict_t *self, edict_t *other, edict_t *activator)
  381. {
  382.     edict_t    *ent;
  383.  
  384.     ent = G_Spawn();
  385.     ent->classname = self->target;
  386.     VectorCopy (self->s.origin, ent->s.origin);
  387.     VectorCopy (self->s.angles, ent->s.angles);
  388.     ED_CallSpawn (ent);
  389.     gi.unlinkentity (ent);
  390.     KillBox (ent);
  391.     gi.linkentity (ent);
  392.     if (self->speed)
  393.         VectorCopy (self->movedir, ent->velocity);
  394. }
  395.  
  396. void SP_target_spawner (edict_t *self)
  397. {
  398.     self->use = use_target_spawner;
  399.     self->svflags = SVF_NOCLIENT;
  400.     if (self->speed)
  401.     {
  402.         G_SetMovedir (self->s.angles, self->movedir);
  403.         VectorScale (self->movedir, self->speed, self->movedir);
  404.     }
  405. }
  406.  
  407. //==========================================================
  408.  
  409. /*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
  410. Fires a blaster bolt in the set direction when triggered.
  411.  
  412. dmg        default is 15
  413. speed    default is 1000
  414. */
  415.  
  416. void use_target_blaster (edict_t *self, edict_t *other, edict_t *activator)
  417. {
  418.     int effect;
  419.  
  420.     if (self->spawnflags & 2)
  421.         effect = 0;
  422.     else if (self->spawnflags & 1)
  423.         effect = EF_HYPERBLASTER;
  424.     else
  425.         effect = EF_BLASTER;
  426.  
  427.     fire_blaster (self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
  428.     gi.sound (self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
  429. }
  430.  
  431. void SP_target_blaster (edict_t *self)
  432. {
  433.     self->use = use_target_blaster;
  434.     G_SetMovedir (self->s.angles, self->movedir);
  435.     self->noise_index = gi.soundindex ("weapons/laser2.wav");
  436.  
  437.     if (!self->dmg)
  438.         self->dmg = 15;
  439.     if (!self->speed)
  440.         self->speed = 1000;
  441.  
  442.     self->svflags = SVF_NOCLIENT;
  443. }
  444.  
  445.  
  446. //==========================================================
  447.  
  448. /*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
  449. Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit.  It is OK to check multiple triggers.  Message, delay, target, and killtarget also work.
  450. */
  451. void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator)
  452. {
  453.     game.serverflags |= self->spawnflags;
  454.     G_FreeEdict (self);
  455. }
  456.  
  457. void SP_target_crosslevel_trigger (edict_t *self)
  458. {
  459.     self->svflags = SVF_NOCLIENT;
  460.     self->use = trigger_crosslevel_trigger_use;
  461. }
  462.  
  463. /*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
  464. Triggered by a trigger_crosslevel elsewhere within a unit.  If multiple triggers are checked, all must be true.  Delay, target and
  465. killtarget also work.
  466.  
  467. "delay"        delay before using targets if the trigger has been activated (default 1)
  468. */
  469. void target_crosslevel_target_think (edict_t *self)
  470. {
  471.     if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
  472.     {
  473.         G_UseTargets (self, self);
  474.         G_FreeEdict (self);
  475.     }
  476. }
  477.  
  478. void SP_target_crosslevel_target (edict_t *self)
  479. {
  480.     if (! self->delay)
  481.         self->delay = 1;
  482.     self->svflags = SVF_NOCLIENT;
  483.  
  484.     self->think = target_crosslevel_target_think;
  485.     self->nextthink = level.time + self->delay;
  486. }
  487.  
  488. //==========================================================
  489.  
  490. /*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
  491. When triggered, fires a laser.  You can either set a target
  492. or a direction.
  493. */
  494.  
  495. void target_laser_think (edict_t *self)
  496. {
  497.     edict_t    *ignore;
  498.     vec3_t    start;
  499.     vec3_t    end;
  500.     trace_t    tr;
  501.     vec3_t    point;
  502.     vec3_t    last_movedir;
  503.     int        count;
  504.  
  505.     if (self->spawnflags & 0x80000000)
  506.         count = 8;
  507.     else
  508.         count = 4;
  509.  
  510.     if (self->enemy)
  511.     {
  512.         VectorCopy (self->movedir, last_movedir);
  513.         VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
  514.         VectorSubtract (point, self->s.origin, self->movedir);
  515.         VectorNormalize (self->movedir);
  516.         if (!VectorCompare(self->movedir, last_movedir))
  517.             self->spawnflags |= 0x80000000;
  518.     }
  519.  
  520.     ignore = self;
  521.     VectorCopy (self->s.origin, start);
  522.     VectorMA (start, 2048, self->movedir, end);
  523.     while(1)
  524.     {
  525.         tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
  526.  
  527.         if (!tr.ent)
  528.             break;
  529.  
  530.         // hurt it if we can
  531.         if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
  532.             T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
  533.  
  534.         // if we hit something that's not a monster or player or is immune to lasers, we're done
  535.         if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
  536.         {
  537.             if (self->spawnflags & 0x80000000)
  538.             {
  539.                 self->spawnflags &= ~0x80000000;
  540.                 gi.WriteByte (svc_temp_entity);
  541.                 gi.WriteByte (TE_LASER_SPARKS);
  542.                 gi.WriteByte (count);
  543.                 gi.WritePosition (tr.endpos);
  544.                 gi.WriteDir (tr.plane.normal);
  545.                 gi.WriteByte (self->s.skinnum);
  546.                 gi.multicast (tr.endpos, MULTICAST_PVS);
  547.             }
  548.             break;
  549.         }
  550.  
  551.         ignore = tr.ent;
  552.         VectorCopy (tr.endpos, start);
  553.     }
  554.  
  555.     VectorCopy (tr.endpos, self->s.old_origin);
  556.  
  557.     self->nextthink = level.time + FRAMETIME;
  558. }
  559.  
  560. void target_laser_on (edict_t *self)
  561. {
  562.     if (!self->activator)
  563.         self->activator = self;
  564.     self->spawnflags |= 0x80000001;
  565.     self->svflags &= ~SVF_NOCLIENT;
  566.     target_laser_think (self);
  567. }
  568.  
  569. void target_laser_off (edict_t *self)
  570. {
  571.     self->spawnflags &= ~1;
  572.     self->svflags |= SVF_NOCLIENT;
  573.     self->nextthink = 0;
  574. }
  575.  
  576. void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
  577. {
  578.     self->activator = activator;
  579.     if (self->spawnflags & 1)
  580.         target_laser_off (self);
  581.     else
  582.         target_laser_on (self);
  583. }
  584.  
  585. void target_laser_start (edict_t *self)
  586. {
  587.     edict_t *ent;
  588.  
  589.     self->movetype = MOVETYPE_NONE;
  590.     self->solid = SOLID_NOT;
  591.     self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
  592.     self->s.modelindex = 1;            // must be non-zero
  593.  
  594.     // set the beam diameter
  595.     if (self->spawnflags & 64)
  596.         self->s.frame = 16;
  597.     else
  598.         self->s.frame = 4;
  599.  
  600.     // set the color
  601.     if (self->spawnflags & 2)
  602.         self->s.skinnum = 0xf2f2f0f0;
  603.     else if (self->spawnflags & 4)
  604.         self->s.skinnum = 0xd0d1d2d3;
  605.     else if (self->spawnflags & 8)
  606.         self->s.skinnum = 0xf3f3f1f1;
  607.     else if (self->spawnflags & 16)
  608.         self->s.skinnum = 0xdcdddedf;
  609.     else if (self->spawnflags & 32)
  610.         self->s.skinnum = 0xe0e1e2e3;
  611.  
  612.     if (!self->enemy)
  613.     {
  614.         if (self->target)
  615.         {
  616.             ent = G_Find (NULL, FOFS(targetname), self->target);
  617.             if (!ent)
  618.                 gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
  619.             self->enemy = ent;
  620.         }
  621.         else
  622.         {
  623.             G_SetMovedir (self->s.angles, self->movedir);
  624.         }
  625.     }
  626.     self->use = target_laser_use;
  627.     self->think = target_laser_think;
  628.  
  629.     if (!self->dmg)
  630.         self->dmg = 1;
  631.  
  632.     VectorSet (self->mins, -8, -8, -8);
  633.     VectorSet (self->maxs, 8, 8, 8);
  634.     gi.linkentity (self);
  635.  
  636.     if (self->spawnflags & 1)
  637.         target_laser_on (self);
  638.     else
  639.         target_laser_off (self);
  640. }
  641.  
  642. void SP_target_laser (edict_t *self)
  643. {
  644.     // let everything else get spawned before we start firing
  645.     self->think = target_laser_start;
  646.     self->nextthink = level.time + 1;
  647. }
  648.  
  649. //==========================================================
  650.  
  651. /*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
  652. speed        How many seconds the ramping will take
  653. message        two letters; starting lightlevel and ending lightlevel
  654. */
  655.  
  656. void target_lightramp_think (edict_t *self)
  657. {
  658.     char    style[2];
  659.  
  660.     style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
  661.     style[1] = 0;
  662.     gi.configstring (CS_LIGHTS+self->enemy->style, style);
  663.  
  664.     if ((level.time - self->timestamp) < self->speed)
  665.     {
  666.         self->nextthink = level.time + FRAMETIME;
  667.     }
  668.     else if (self->spawnflags & 1)
  669.     {
  670.         char    temp;
  671.  
  672.         temp = self->movedir[0];
  673.         self->movedir[0] = self->movedir[1];
  674.         self->movedir[1] = temp;
  675.         self->movedir[2] *= -1;
  676.     }
  677. }
  678.  
  679. void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
  680. {
  681.     if (!self->enemy)
  682.     {
  683.         edict_t        *e;
  684.  
  685.         // check all the targets
  686.         e = NULL;
  687.         while (1)
  688.         {
  689.             e = G_Find (e, FOFS(targetname), self->target);
  690.             if (!e)
  691.                 break;
  692.             if (strcmp(e->classname, "light") != 0)
  693.             {
  694.                 gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
  695.                 gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
  696.             }
  697.             else
  698.             {
  699.                 self->enemy = e;
  700.             }
  701.         }
  702.  
  703.         if (!self->enemy)
  704.         {
  705.             gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
  706.             G_FreeEdict (self);
  707.             return;
  708.         }
  709.     }
  710.  
  711.     self->timestamp = level.time;
  712.     target_lightramp_think (self);
  713. }
  714.  
  715. void SP_target_lightramp (edict_t *self)
  716. {
  717.     if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1])
  718.     {
  719.         gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
  720.         G_FreeEdict (self);
  721.         return;
  722.     }
  723.  
  724.     if (deathmatch->value)
  725.     {
  726.         G_FreeEdict (self);
  727.         return;
  728.     }
  729.  
  730.     if (!self->target)
  731.     {
  732.         gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
  733.         G_FreeEdict (self);
  734.         return;
  735.     }
  736.  
  737.     self->svflags |= SVF_NOCLIENT;
  738.     self->use = target_lightramp_use;
  739.     self->think = target_lightramp_think;
  740.  
  741.     self->movedir[0] = self->message[0] - 'a';
  742.     self->movedir[1] = self->message[1] - 'a';
  743.     self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
  744. }
  745.  
  746. //==========================================================
  747.  
  748. /*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
  749. When triggered, this initiates a level-wide earthquake.
  750. All players and monsters are affected.
  751. "speed"        severity of the quake (default:200)
  752. "count"        duration of the quake (default:5)
  753. */
  754.  
  755. void target_earthquake_think (edict_t *self)
  756. {
  757.     int        i;
  758.     edict_t    *e;
  759.  
  760.     if (self->last_move_time < level.time)
  761.     {
  762.         gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
  763.         self->last_move_time = level.time + 0.5;
  764.     }
  765.  
  766.     for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
  767.     {
  768.         if (!e->inuse)
  769.             continue;
  770.         if (!e->client)
  771.             continue;
  772.         if (!e->groundentity)
  773.             continue;
  774.  
  775.         e->groundentity = NULL;
  776.         e->velocity[0] += crandom()* 150;
  777.         e->velocity[1] += crandom()* 150;
  778.         e->velocity[2] = self->speed * (100.0 / e->mass);
  779.     }
  780.  
  781.     if (level.time < self->timestamp)
  782.         self->nextthink = level.time + FRAMETIME;
  783. }
  784.  
  785. void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
  786. {
  787.     self->timestamp = level.time + self->count;
  788.     self->nextthink = level.time + FRAMETIME;
  789.     self->activator = activator;
  790.     self->last_move_time = 0;
  791. }
  792.  
  793. void SP_target_earthquake (edict_t *self)
  794. {
  795.     if (!self->targetname)
  796.         gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
  797.  
  798.     if (!self->count)
  799.         self->count = 5;
  800.  
  801.     if (!self->speed)
  802.         self->speed = 200;
  803.  
  804.     self->svflags |= SVF_NOCLIENT;
  805.     self->think = target_earthquake_think;
  806.     self->use = target_earthquake_use;
  807.  
  808.     self->noise_index = gi.soundindex ("world/quake.wav");
  809. }
  810.